/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022.
 * Description: rq clock corrupt module initialization
 * Author: yangzhuohao <yangzhuohao1@huawei.com>
 * Create: 2022-09-13
 */
#include <linux/types.h>
#include <linux/hal/acpi_op.h>
#include <linux/rtos_acpi_id.h>
#include <linux/percpu-defs.h>
#include "sched.h"
#include "rq_clock_corrupt_dfx.h"

static DEFINE_PER_CPU(u64, last_rq_clock);
bool rq_clock_corrupt_check_enable;

/*
 * rq->clock only update in update_rq_clock(). so we add a check here.
 * Use the percpu variable to check whether the rq->clock has been changed.
 * If the rq->clock has changed, print the changed value.
 */
void check_rq_clock(struct rq *rq)
{
	u64 last_clock;
	static unsigned int times;

	if (times >= MAX_PRINT_TIMES)
		return;

	last_clock = per_cpu(last_rq_clock, cpu_of(rq));
	/* rq->clock is not changed */
	if (likely(last_clock == rq->clock))
		return;

	/* last_clock is 0 at first, not a real error */
	if (unlikely(last_clock == 0))
		return;

	pr_err("rq->clock is corrupt, old_value:%llu corrupt_value:%llu\n",
		last_clock, rq->clock);
	times++;
}

void update_last_clock(struct rq *rq)
{
	per_cpu(last_rq_clock, cpu_of(rq)) = rq->clock;
}

static acpi_status rq_clock_corrupt_dfx_acpi_init(acpi_handle handle, u32 level,
	void *context, void **retval)
{
	int ret;
	struct acpi_device device;
	u64 data;

	acpi_get_acpi_device(handle, &device, RTOS_ACPI_HID_TIME_JUMP_DFX);
	if (device.handle != handle)
		return AE_ERROR;

	ret = acpi_getprop_u64(&data, &device, "rq_clock_corrupt_check");
	if (ret < 0)
		return AE_ERROR;
	if (data != 0 && data != 1) {
		pr_err("rq clock corrupt dfx: invalid parameter\n");
		return AE_ERROR;
	}

	rq_clock_corrupt_check_enable = (data == 1);
	pr_info("rq clock corrupt dfx init enabled\n");
	return AE_OK;
}

static int rq_clock_corrupt_dfx_init(void)
{
	if (acpi_disabled)
		return -ENODEV;

	(void)acpi_get_devices(RTOS_ACPI_HID_TIME_JUMP_DFX, rq_clock_corrupt_dfx_acpi_init, NULL, NULL);
	/*
	 * if no device is found in acpi_get_devices(), AE_OK is still returned.
	 * therefore, use other methods to check whether the acpi configuration
	 * takes effect.
	 */
	if (!rq_clock_corrupt_check_enable)
		return -ENODEV;
	return 0;
}
module_init(rq_clock_corrupt_dfx_init);
